﻿// ===============================================================================================
//  The MIT License (MIT) for AsciiFBXExporter
// 
//  ASCII FBX Exporter (formerly called the Unity FBX Exporter) was created for 
//  Building Crafter (http://u3d.as/ovC), a tool to rapidly create high quality 
//  buildings right in Unity with no need to use 3D modeling programs.
//
//  Copyright (c) 2016 - 2023 | 8Bit Goose Games, Inc.
//  	
//  Permission is hereby granted, free of charge, to any person obtaining a copy 
//  of this software and associated documentation files (the "Software"), to deal 
//  in the Software without restriction, including without limitation the rights 
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
//  of the Software, and to permit persons to whom the Software is furnished to do so, 
//  subject to the following conditions:
//  	
//  The above copyright notice and this permission notice shall be included in all 
//  copies or substantial portions of the Software.
//  	
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
//  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
//  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
//  OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//  
// ===============================================================================================

using UnityEngine;
using System.Collections;
using System.Text;
using System.Collections.Generic;
using FE = AsciiFBXExporter.FBXExporter;

namespace AsciiFBXExporter
{
	public class FBXUnityMeshGetter
	{
        /// <summary>
        /// Gets all the meshes and outputs to a string (even grabbing the child of each gameObject)
        /// </summary>
        /// <returns>The mesh to string.</returns>
        /// <param name="gameObj">GameObject Parent.</param>
        /// <param name="materials">Every Material in the parent that can be accessed.</param>
        /// <param name="objects">The StringBuidler to create objects for the FBX file.</param>
        /// <param name="connections">The StringBuidler to create connections for the FBX file.</param>
        /// <param name="parentObject">Parent object, if left null this is the top parent.</param>
        /// <param name="parentModelId">Parent model id, 0 if top parent.</param>
        public static long GetMeshToString(GameObject gameObj,
                                           Material[] materials,
                                           ref StringBuilder objects,
                                           ref StringBuilder connections,
                                           GameObject parentObject = null,
                                           long parentModelId = 0)
        {
            StringBuilder tempObjectSb = new StringBuilder();
            StringBuilder tempConnectionsSb = new StringBuilder();

            long geometryId = FBXExporter.GetRandomFBXId();
            long modelId = FBXExporter.GetRandomFBXId();

			// Sees if there is a mesh to export and add to the system
			MeshFilter filter = gameObj.GetComponent<MeshFilter>();
			SkinnedMeshRenderer skinnedMesh = gameObj.GetComponent<SkinnedMeshRenderer>();

			// The mesh to export is this level's mesh that is going to be exported
			Mesh meshToExport = null;
			if(filter != null)
				meshToExport = filter.sharedMesh;
			else if(skinnedMesh != null) // If this object has a skinned mesh on it, bake that mesh into whatever pose it is at and add it as a new mesh to export
			{
				meshToExport = new Mesh();
				skinnedMesh.BakeMesh(meshToExport);
			}

			string meshName = gameObj.name;

			// A NULL parent means that the gameObject is at the top
			string isMesh = "Null";

			if(meshToExport != null)
			{
				meshName = meshToExport.name;
				isMesh = "Mesh";
			}

            if (filter != null)
			{
				if(filter.sharedMesh == null)
				{
					// The MeshFilter has no mesh assigned, so treat it like an FBX Null node.
					filter = null;
				}
				else
				{
					meshName = filter.sharedMesh.name;
					isMesh = "Mesh";
				}
			}

			// If we've got a skinned mesh without a name, give it a random name
			if(meshName == "" && skinnedMesh != null)
				meshName = "Skinned Mesh " + Random.Range(0, 1000000);

			if(parentModelId == 0)
				tempConnectionsSb.AppendLine("\t;Model::" + meshName + ", Model::RootNode");
			else
				tempConnectionsSb.AppendLine("\t;Model::" + meshName + ", Model::USING PARENT");
			tempConnectionsSb.AppendLine("\tC: \"OO\"," + modelId + "," + parentModelId);
			tempConnectionsSb.AppendLine();
			tempObjectSb.AppendLine("\tModel: " + modelId + ", \"Model::" + gameObj.name + "\", \"" + isMesh + "\" {");
			tempObjectSb.AppendLine("\t\tVersion: 232");
			tempObjectSb.AppendLine("\t\tProperties70:  {");
			tempObjectSb.AppendLine("\t\t\tP: \"RotationOrder\", \"enum\", \"\", \"\",4");
			tempObjectSb.AppendLine("\t\t\tP: \"RotationActive\", \"bool\", \"\", \"\",1");
			tempObjectSb.AppendLine("\t\t\tP: \"InheritType\", \"enum\", \"\", \"\",1");
			tempObjectSb.AppendLine("\t\t\tP: \"ScalingMax\", \"Vector3D\", \"Vector\", \"\",0,0,0");
			tempObjectSb.AppendLine("\t\t\tP: \"DefaultAttributeIndex\", \"int\", \"Integer\", \"\",0");
			// ===== Local Translation Offset =========
			Vector3 position = gameObj.transform.localPosition;

			tempObjectSb.Append("\t\t\tP: \"Lcl Translation\", \"Lcl Translation\", \"\", \"A+\",");

			// Append the X Y Z coords to the system
			tempObjectSb.AppendFormat("{0},{1},{2}", FE.FBXFormat(position.x * -1), FE.FBXFormat(position.y), FE.FBXFormat(position.z));
			tempObjectSb.AppendLine();

			// Rotates the object correctly from Unity space
			Vector3 localRotation = gameObj.transform.localEulerAngles;
			tempObjectSb.AppendFormat("\t\t\tP: \"Lcl Rotation\", \"Lcl Rotation\", \"\", \"A+\",{0},{1},{2}", FE.FBXFormat(localRotation.x), FE.FBXFormat(localRotation.y * -1), FE.FBXFormat(-1 * localRotation.z));
			tempObjectSb.AppendLine();

			// Adds the local scale of this object
		    Vector3 localScale = gameObj.transform.localScale;
			tempObjectSb.AppendFormat("\t\t\tP: \"Lcl Scaling\", \"Lcl Scaling\", \"\", \"A\",{0},{1},{2}", FE.FBXFormat(localScale.x), FE.FBXFormat(localScale.y), FE.FBXFormat(localScale.z));
			tempObjectSb.AppendLine();

			tempObjectSb.AppendLine("\t\t\tP: \"currentUVSet\", \"KString\", \"\", \"U\", \"map1\"");
			tempObjectSb.AppendLine("\t\t}");
			tempObjectSb.AppendLine("\t\tShading: T");
			tempObjectSb.AppendLine("\t\tCulling: \"CullingOff\"");
			tempObjectSb.AppendLine("\t}");

			// Adds in geometry if it exists, if it it does not exist, this is a empty gameObject file and skips over creating mesh links and geometry
			if(meshToExport != null)
			{
				Mesh mesh = meshToExport;

				// =================================
				//         General Geometry Info
				// =================================
				// Generate the geometry information for the mesh created

				tempObjectSb.AppendLine("\tGeometry: " + geometryId + ", \"Geometry::\", \"Mesh\" {");
				
				// ===== WRITE THE VERTICIES =====
				Vector3[] verticies = mesh.vertices;
				int vertCount = mesh.vertexCount * 3; // <= because the list of points is just a list of comma seperated values, we need to multiply by three

				tempObjectSb.AppendLine("\t\tVertices: *" + vertCount + " {");
				tempObjectSb.Append("\t\t\ta: ");
				for(int i = 0; i < verticies.Length; i++)
				{
					if(i > 0)
						tempObjectSb.Append(",");

					// Points in the verticies. We also reverse the x value because Unity has a reverse X coordinate
					tempObjectSb.AppendFormat("{0},{1},{2}", FE.FBXFormat(verticies[i].x * -1), FE.FBXFormat(verticies[i].y), FE.FBXFormat(verticies[i].z));
				}

				tempObjectSb.AppendLine();
				tempObjectSb.AppendLine("\t\t} ");
				
				// ======= WRITE THE TRIANGLES ========
				int triangleCount = mesh.triangles.Length;
				int[] triangles = mesh.triangles;

				tempObjectSb.AppendLine("\t\tPolygonVertexIndex: *" + triangleCount + " {");

				// Write triangle indexes
				tempObjectSb.Append("\t\t\ta: ");
				for(int i = 0; i < triangleCount; i += 3)
				{
					if(i > 0)
						tempObjectSb.Append(",");

					// To get the correct normals, must rewind the triangles since we flipped the x direction
					tempObjectSb.AppendFormat("{0},{1},{2}", 
					                          triangles[i],
					                          triangles[i + 2], 
					                          (triangles[i + 1] * -1) - 1); // <= Tells the poly is ended
				}

				tempObjectSb.AppendLine();

				tempObjectSb.AppendLine("\t\t} ");
				tempObjectSb.AppendLine("\t\tGeometryVersion: 124");
				tempObjectSb.AppendLine("\t\tLayerElementNormal: 0 {");
				tempObjectSb.AppendLine("\t\t\tVersion: 101");
				tempObjectSb.AppendLine("\t\t\tName: \"\"");
				tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
				tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"Direct\"");
				
				// ===== WRITE THE NORMALS ==========
				Vector3[] normals = mesh.normals;

				tempObjectSb.AppendLine("\t\t\tNormals: *" + (triangleCount * 3) + " {");
				tempObjectSb.Append("\t\t\t\ta: ");

				for(int i = 0; i < triangleCount; i += 3)
				{
					if(i > 0)
						tempObjectSb.Append(",");

					// To get the correct normals, must rewind the normal triangles like the triangles above since x was flipped
					Vector3 newNormal = normals[triangles[i]];

					tempObjectSb.AppendFormat("{0},{1},{2},",
											 FE.FBXFormat(newNormal.x * -1), // Switch normal as is tradition
											 FE.FBXFormat(newNormal.y),
											 FE.FBXFormat(newNormal.z));

					newNormal = normals[triangles[i + 2]];

					tempObjectSb.AppendFormat("{0},{1},{2},",
											 FE.FBXFormat(newNormal.x * -1), // Switch normal as is tradition
											 FE.FBXFormat(newNormal.y),
											 FE.FBXFormat(newNormal.z));

					newNormal = normals[triangles[i + 1]];

					tempObjectSb.AppendFormat("{0},{1},{2}",
											 FE.FBXFormat(newNormal.x * -1), // Switch normal as is tradition
											 FE.FBXFormat(newNormal.y),
											 FE.FBXFormat(newNormal.z));
				}

				tempObjectSb.AppendLine();
				tempObjectSb.AppendLine("\t\t\t}");
				tempObjectSb.AppendLine("\t\t}");
				
				// ===== WRITE THE COLORS =====
				bool containsColors = mesh.colors.Length == verticies.Length;
				
				if(containsColors)
				{
					Color[] colors = mesh.colors;
                
					Dictionary<Color, int> colorTable = new Dictionary<Color, int>(); // reducing amount of data by only keeping unique colors.
					int idx = 0;

					// build index table of all the different colors present in the mesh            
					for (int i = 0; i < colors.Length; i++)
					{
						if (!colorTable.ContainsKey(colors[i]))
						{
							colorTable[colors[i]] = idx;
							idx++;
						}
					}

					tempObjectSb.AppendLine("\t\tLayerElementColor: 0 {");
					tempObjectSb.AppendLine("\t\t\tVersion: 101");
					tempObjectSb.AppendLine("\t\t\tName: \"Col\"");
					tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
					tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");
					tempObjectSb.AppendLine("\t\t\tColors: *" + colorTable.Count * 4 + " {");
					tempObjectSb.Append("\t\t\t\ta: ");

					bool first = true;
					foreach (KeyValuePair<Color, int> color in colorTable)
					{
						if (!first)
							tempObjectSb.Append(",");

						tempObjectSb.AppendFormat("{0},{1},{2},{3}", FE.FBXFormat(color.Key.r), FE.FBXFormat(color.Key.g), FE.FBXFormat(color.Key.b), FE.FBXFormat(color.Key.a));
						first = false;
					}
					tempObjectSb.AppendLine();

					tempObjectSb.AppendLine("\t\t\t}");

					// Color index
					tempObjectSb.AppendLine("\t\t\tColorIndex: *" + triangles.Length + " {");
					tempObjectSb.Append("\t\t\t\ta: ");

					for (int i = 0; i < triangles.Length; i += 3)
					{
						if (i > 0)
							tempObjectSb.Append(",");

						// Triangles need to be fliped for the x flip
						int index1 = triangles[i];
						int index2 = triangles[i + 2];
						int index3 = triangles[i + 1];

						// Find the color index related to that vertice index
						index1 = colorTable[colors[index1]];
						index2 = colorTable[colors[index2]];
						index3 = colorTable[colors[index3]];

						tempObjectSb.AppendFormat("{0},{1},{2}", index1, index2, index3);
					}

					tempObjectSb.AppendLine();

					tempObjectSb.AppendLine("\t\t\t}");
					tempObjectSb.AppendLine("\t\t}");
				}

				// ================ UV CREATION =========================

				// -- UV 1 Creation
				int uvLength = mesh.uv.Length;
				Vector2[] uvs = mesh.uv;

				tempObjectSb.AppendLine("\t\tLayerElementUV: 0 {"); // the Zero here is for the first UV map
				tempObjectSb.AppendLine("\t\t\tVersion: 101");
				tempObjectSb.AppendLine("\t\t\tName: \"UVSet0\"");
				tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
				tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");
				tempObjectSb.AppendLine("\t\t\tUV: *" + uvLength * 2 + " {");
				tempObjectSb.Append("\t\t\t\ta: ");

				for(int i = 0; i < uvLength; i++)
				{
					if(i > 0)
						tempObjectSb.Append(",");

					tempObjectSb.AppendFormat("{0},{1}", FE.FBXFormat(uvs[i].x), FE.FBXFormat(uvs[i].y));
				}
				tempObjectSb.AppendLine();

				tempObjectSb.AppendLine("\t\t\t}");

				// UV tile index coords
				tempObjectSb.AppendLine("\t\t\tUVIndex: *" + triangleCount +" {");
				tempObjectSb.Append("\t\t\t\ta: ");

				for(int i = 0; i < triangleCount; i += 3)
				{
					if(i > 0)
						tempObjectSb.Append(",");

					// Triangles need to be fliped for the x flip
					int index1 = triangles[i];
					int index2 = triangles[i + 2];
					int index3 = triangles[i + 1];

					tempObjectSb.AppendFormat("{0},{1},{2}", index1, index2, index3);
				}

				tempObjectSb.AppendLine();

				tempObjectSb.AppendLine("\t\t\t}");
				tempObjectSb.AppendLine("\t\t}");

                // -- UV 2 Creation
                if (mesh.uv2 != null && mesh.uv2.Length != 0)
                {
                    uvLength = mesh.uv2.Length;
                    uvs = mesh.uv2;

                    tempObjectSb.AppendLine("\t\tLayerElementUV: 1 {"); // the Zero here is for the first UV map
                    tempObjectSb.AppendLine("\t\t\tVersion: 101");
                    tempObjectSb.AppendLine("\t\t\tName: \"UVSet1\"");
                    tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
                    tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");
                    tempObjectSb.AppendLine("\t\t\tUV: *" + uvLength * 2 + " {");
                    tempObjectSb.Append("\t\t\t\ta: ");

                    for (int i = 0; i < uvLength; i++)
                    {
                        if (i > 0)
                            tempObjectSb.Append(",");

                        tempObjectSb.AppendFormat("{0},{1}", FE.FBXFormat(uvs[i].x), FE.FBXFormat(uvs[i].y));

                    }
                    tempObjectSb.AppendLine();

                    tempObjectSb.AppendLine("\t\t\t}");

                    // UV tile index coords
                    tempObjectSb.AppendLine("\t\t\tUVIndex: *" + triangleCount + " {");
                    tempObjectSb.Append("\t\t\t\ta: ");

                    for (int i = 0; i < triangleCount; i += 3)
                    {
                        if (i > 0)
                            tempObjectSb.Append(",");

                        // Triangles need to be fliped for the x flip
                        int index1 = triangles[i];
                        int index2 = triangles[i + 2];
                        int index3 = triangles[i + 1];

                        tempObjectSb.AppendFormat("{0},{1},{2}", index1, index2, index3);
                    }

                    tempObjectSb.AppendLine();

                    tempObjectSb.AppendLine("\t\t\t}");
                    tempObjectSb.AppendLine("\t\t}");
                }

				// TODO: Add more UV saving if FBX supports it

                // -- Smoothing
                // TODO: Smoothing doesn't seem to do anything when importing. This maybe should be added. -KBH

                // ============ MATERIALS =============

                tempObjectSb.AppendLine("\t\tLayerElementMaterial: 0 {");
				tempObjectSb.AppendLine("\t\t\tVersion: 101");
				tempObjectSb.AppendLine("\t\t\tName: \"\"");
				tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygon\"");
				tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");

				int totalFaceCount = 0;

				// So by polygon means that we need 1/3rd of how many indicies we wrote.
				int numberOfSubmeshes = mesh.subMeshCount;

				StringBuilder submeshesSb = new StringBuilder();

				// For just one submesh, we set them all to zero
				if(numberOfSubmeshes == 1)
				{
					int numFaces = triangles.Length / 3;

					for(int i = 0; i < numFaces; i++)
					{
						submeshesSb.Append("0,");
						totalFaceCount++;
					}
				}
				else
				{
					List<int[]> allSubmeshes = new List<int[]>();
					
					// Load all submeshes into a space
					for(int i = 0; i < numberOfSubmeshes; i++)
						allSubmeshes.Add(mesh.GetIndices(i));

					// TODO: Optimize this search pattern
					for(int i = 0; i < triangles.Length; i += 3)
					{
						for(int subMeshIndex = 0; subMeshIndex < allSubmeshes.Count; subMeshIndex++)
						{
							bool breaker = false;
							
							for(int n = 0; n < allSubmeshes[subMeshIndex].Length; n += 3)
							{
								if(triangles[i] == allSubmeshes[subMeshIndex][n]
								   && triangles[i + 1] == allSubmeshes[subMeshIndex][n + 1]
								   && triangles[i + 2] == allSubmeshes[subMeshIndex][n + 2])
								{
									submeshesSb.Append(subMeshIndex.ToString());
									submeshesSb.Append(",");
									totalFaceCount++;
									break;
								}
								
								if(breaker)
									break;
							}
						}
					}

					Debug.Log(submeshesSb[submeshesSb.Length - 1]);

					if(submeshesSb[submeshesSb.Length - 1] == ',')
						submeshesSb.Remove(submeshesSb.Length - 1, 1);
				}

				tempObjectSb.AppendLine("\t\t\tMaterials: *" + totalFaceCount + " {");
				tempObjectSb.Append("\t\t\t\ta: ");
				tempObjectSb.AppendLine(submeshesSb.ToString());
				tempObjectSb.AppendLine("\t\t\t} ");
				tempObjectSb.AppendLine("\t\t}");

				// ============= INFORMS WHAT TYPE OF LATER ELEMENTS ARE IN THIS GEOMETRY =================
				tempObjectSb.AppendLine("\t\tLayer: 0 {");
				tempObjectSb.AppendLine("\t\t\tVersion: 100");
				tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
				tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementNormal\"");
				tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
				tempObjectSb.AppendLine("\t\t\t}");
				tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
				tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementMaterial\"");
				tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
				tempObjectSb.AppendLine("\t\t\t}");
				tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
				tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementTexture\"");
				tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
				tempObjectSb.AppendLine("\t\t\t}");
				if(containsColors)
				{
					tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
					tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementColor\"");
					tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
					tempObjectSb.AppendLine("\t\t\t}");
				}
				tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
				tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementUV\"");
				tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
				tempObjectSb.AppendLine("\t\t\t}");
                // inserts UV2s into the file export
                if (mesh.uv2 != null && mesh.uv2.Length != 0)
                {
                    tempObjectSb.AppendLine("\t\t}");
                    tempObjectSb.AppendLine("\t\tLayer: 1 {");
                    tempObjectSb.AppendLine("\t\t\tVersion: 100");
                    tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
                    tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementUV\"");
                    tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 1");
                    tempObjectSb.AppendLine("\t\t\t}");
                }
                tempObjectSb.AppendLine("\t\t}");
				tempObjectSb.AppendLine("\t}");

				// Add the connection for the model to the geometry so it is attached the right mesh
				tempConnectionsSb.AppendLine("\t;Geometry::, Model::" + mesh.name);
				tempConnectionsSb.AppendLine("\tC: \"OO\"," + geometryId + "," + modelId);
				tempConnectionsSb.AppendLine();

				// Add the connection of all the materials in order of submesh
				MeshRenderer meshRenderer = gameObj.GetComponent<MeshRenderer>();
				if(meshRenderer != null)
				{
					Material[] allMaterialsInThisMesh = meshRenderer.sharedMaterials;

					for(int i = 0; i < allMaterialsInThisMesh.Length; i++)
					{
						Material mat = allMaterialsInThisMesh[i];						
		
						if(mat == null)
						{
							Debug.LogError("ERROR: the game object " + gameObj.name + " has an empty material on it. This will export problematic files. Please fix and reexport");
							continue;
						}

                        int referenceId = Mathf.Abs(mat.GetInstanceID());

                        tempConnectionsSb.AppendLine("\t;Material::" + mat.name + ", Model::" + mesh.name);
						tempConnectionsSb.AppendLine("\tC: \"OO\"," + referenceId + "," + modelId);
						tempConnectionsSb.AppendLine();
					}
				}

			}

			// Recursively add all the other objects to the string that has been built.
			for(int i = 0; i < gameObj.transform.childCount; i++)
			{
				GameObject childObject = gameObj.transform.GetChild(i).gameObject;
				FBXUnityMeshGetter.GetMeshToString(childObject, materials, ref tempObjectSb, ref tempConnectionsSb, gameObj, modelId);
			}

			objects.Append(tempObjectSb.ToString());
			connections.Append(tempConnectionsSb.ToString());

			return modelId;
		}


		public static long GetMeshToString(Mesh meshToExport,
										   ref StringBuilder objects,
										   ref StringBuilder connections  )
		{
			StringBuilder tempObjectSb = new StringBuilder();
			StringBuilder tempConnectionsSb = new StringBuilder();

			long geometryId = FBXExporter.GetRandomFBXId();
			long modelId = FBXExporter.GetRandomFBXId();

			// Sees if there is a mesh to export and add to the system
 
			SkinnedMeshRenderer skinnedMesh = null;

			// The mesh to export is this level's mesh that is going to be exported
		 
			 

			string meshName = "";

			// A NULL parent means that the gameObject is at the top
			string isMesh = "Null";

			if (meshToExport != null)
			{
				meshName = meshToExport.name;
				isMesh = "Mesh";
			}

			 

			// If we've got a skinned mesh without a name, give it a random name
			if (meshName == "" && skinnedMesh != null)
				meshName = "Skinned Mesh " + Random.Range(0, 1000000);

		 
			tempConnectionsSb.AppendLine("\t;Model::" + meshName + ", Model::RootNode");
			 
			tempConnectionsSb.AppendLine("\tC: \"OO\"," + modelId + "," + 0);
			tempConnectionsSb.AppendLine();
			tempObjectSb.AppendLine("\tModel: " + modelId + ", \"Model::" + meshToExport.name + "\", \"" + isMesh + "\" {");
			tempObjectSb.AppendLine("\t\tVersion: 232");
			tempObjectSb.AppendLine("\t\tProperties70:  {");
			tempObjectSb.AppendLine("\t\t\tP: \"RotationOrder\", \"enum\", \"\", \"\",4");
			tempObjectSb.AppendLine("\t\t\tP: \"RotationActive\", \"bool\", \"\", \"\",1");
			tempObjectSb.AppendLine("\t\t\tP: \"InheritType\", \"enum\", \"\", \"\",1");
			tempObjectSb.AppendLine("\t\t\tP: \"ScalingMax\", \"Vector3D\", \"Vector\", \"\",0,0,0");
			tempObjectSb.AppendLine("\t\t\tP: \"DefaultAttributeIndex\", \"int\", \"Integer\", \"\",0");
			// ===== Local Translation Offset =========
			Vector3 position = Vector3.zero;

			tempObjectSb.Append("\t\t\tP: \"Lcl Translation\", \"Lcl Translation\", \"\", \"A+\",");

			// Append the X Y Z coords to the system
			tempObjectSb.AppendFormat("{0},{1},{2}", FE.FBXFormat(position.x * -1), FE.FBXFormat(position.y), FE.FBXFormat(position.z));
			tempObjectSb.AppendLine();

			// Rotates the object correctly from Unity space
			Vector3 localRotation = Vector3.zero;
			tempObjectSb.AppendFormat("\t\t\tP: \"Lcl Rotation\", \"Lcl Rotation\", \"\", \"A+\",{0},{1},{2}", FE.FBXFormat(localRotation.x), FE.FBXFormat(localRotation.y * -1), FE.FBXFormat(-1 * localRotation.z));
			tempObjectSb.AppendLine();

			// Adds the local scale of this object
			Vector3 localScale = Vector3.one;
			tempObjectSb.AppendFormat("\t\t\tP: \"Lcl Scaling\", \"Lcl Scaling\", \"\", \"A\",{0},{1},{2}", FE.FBXFormat(localScale.x), FE.FBXFormat(localScale.y), FE.FBXFormat(localScale.z));
			tempObjectSb.AppendLine();

			tempObjectSb.AppendLine("\t\t\tP: \"currentUVSet\", \"KString\", \"\", \"U\", \"map1\"");
			tempObjectSb.AppendLine("\t\t}");
			tempObjectSb.AppendLine("\t\tShading: T");
			tempObjectSb.AppendLine("\t\tCulling: \"CullingOff\"");
			tempObjectSb.AppendLine("\t}");

			// Adds in geometry if it exists, if it it does not exist, this is a empty gameObject file and skips over creating mesh links and geometry
			if (meshToExport != null)
			{
				Mesh mesh = meshToExport;

				// =================================
				//         General Geometry Info
				// =================================
				// Generate the geometry information for the mesh created

				tempObjectSb.AppendLine("\tGeometry: " + geometryId + ", \"Geometry::\", \"Mesh\" {");

				// ===== WRITE THE VERTICIES =====
				Vector3[] verticies = mesh.vertices;
				int vertCount = mesh.vertexCount * 3; // <= because the list of points is just a list of comma seperated values, we need to multiply by three

				tempObjectSb.AppendLine("\t\tVertices: *" + vertCount + " {");
				tempObjectSb.Append("\t\t\ta: ");
				for (int i = 0; i < verticies.Length; i++)
				{
					if (i > 0)
						tempObjectSb.Append(",");

					// Points in the verticies. We also reverse the x value because Unity has a reverse X coordinate
					tempObjectSb.AppendFormat("{0},{1},{2}", FE.FBXFormat(verticies[i].x * -1), FE.FBXFormat(verticies[i].y), FE.FBXFormat(verticies[i].z));
				}

				tempObjectSb.AppendLine();
				tempObjectSb.AppendLine("\t\t} ");

				// ======= WRITE THE TRIANGLES ========
				int triangleCount = mesh.triangles.Length;
				int[] triangles = mesh.triangles;

				tempObjectSb.AppendLine("\t\tPolygonVertexIndex: *" + triangleCount + " {");

				// Write triangle indexes
				tempObjectSb.Append("\t\t\ta: ");
				for (int i = 0; i < triangleCount; i += 3)
				{
					if (i > 0)
						tempObjectSb.Append(",");

					// To get the correct normals, must rewind the triangles since we flipped the x direction
					tempObjectSb.AppendFormat("{0},{1},{2}",
											  triangles[i],
											  triangles[i + 2],
											  (triangles[i + 1] * -1) - 1); // <= Tells the poly is ended
				}

				tempObjectSb.AppendLine();

				tempObjectSb.AppendLine("\t\t} ");
				tempObjectSb.AppendLine("\t\tGeometryVersion: 124");
				tempObjectSb.AppendLine("\t\tLayerElementNormal: 0 {");
				tempObjectSb.AppendLine("\t\t\tVersion: 101");
				tempObjectSb.AppendLine("\t\t\tName: \"\"");
				tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
				tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"Direct\"");

				// ===== WRITE THE NORMALS ==========
				Vector3[] normals = mesh.normals;

				tempObjectSb.AppendLine("\t\t\tNormals: *" + (triangleCount * 3) + " {");
				tempObjectSb.Append("\t\t\t\ta: ");

				for (int i = 0; i < triangleCount; i += 3)
				{
					if (i > 0)
						tempObjectSb.Append(",");

					// To get the correct normals, must rewind the normal triangles like the triangles above since x was flipped
					Vector3 newNormal = normals[triangles[i]];

					tempObjectSb.AppendFormat("{0},{1},{2},",
											 FE.FBXFormat(newNormal.x * -1), // Switch normal as is tradition
											 FE.FBXFormat(newNormal.y),
											 FE.FBXFormat(newNormal.z));

					newNormal = normals[triangles[i + 2]];

					tempObjectSb.AppendFormat("{0},{1},{2},",
											 FE.FBXFormat(newNormal.x * -1), // Switch normal as is tradition
											 FE.FBXFormat(newNormal.y),
											 FE.FBXFormat(newNormal.z));

					newNormal = normals[triangles[i + 1]];

					tempObjectSb.AppendFormat("{0},{1},{2}",
											 FE.FBXFormat(newNormal.x * -1), // Switch normal as is tradition
											 FE.FBXFormat(newNormal.y),
											 FE.FBXFormat(newNormal.z));
				}

				tempObjectSb.AppendLine();
				tempObjectSb.AppendLine("\t\t\t}");
				tempObjectSb.AppendLine("\t\t}");

				// ===== WRITE THE COLORS =====
				bool containsColors = mesh.colors.Length == verticies.Length;

				if (containsColors)
				{
					Color[] colors = mesh.colors;

					Dictionary<Color, int> colorTable = new Dictionary<Color, int>(); // reducing amount of data by only keeping unique colors.
					int idx = 0;

					// build index table of all the different colors present in the mesh            
					for (int i = 0; i < colors.Length; i++)
					{
						if (!colorTable.ContainsKey(colors[i]))
						{
							colorTable[colors[i]] = idx;
							idx++;
						}
					}

					tempObjectSb.AppendLine("\t\tLayerElementColor: 0 {");
					tempObjectSb.AppendLine("\t\t\tVersion: 101");
					tempObjectSb.AppendLine("\t\t\tName: \"Col\"");
					tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
					tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");
					tempObjectSb.AppendLine("\t\t\tColors: *" + colorTable.Count * 4 + " {");
					tempObjectSb.Append("\t\t\t\ta: ");

					bool first = true;
					foreach (KeyValuePair<Color, int> color in colorTable)
					{
						if (!first)
							tempObjectSb.Append(",");

						tempObjectSb.AppendFormat("{0},{1},{2},{3}", FE.FBXFormat(color.Key.r), FE.FBXFormat(color.Key.g), FE.FBXFormat(color.Key.b), FE.FBXFormat(color.Key.a));
						first = false;
					}
					tempObjectSb.AppendLine();

					tempObjectSb.AppendLine("\t\t\t}");

					// Color index
					tempObjectSb.AppendLine("\t\t\tColorIndex: *" + triangles.Length + " {");
					tempObjectSb.Append("\t\t\t\ta: ");

					for (int i = 0; i < triangles.Length; i += 3)
					{
						if (i > 0)
							tempObjectSb.Append(",");

						// Triangles need to be fliped for the x flip
						int index1 = triangles[i];
						int index2 = triangles[i + 2];
						int index3 = triangles[i + 1];

						// Find the color index related to that vertice index
						index1 = colorTable[colors[index1]];
						index2 = colorTable[colors[index2]];
						index3 = colorTable[colors[index3]];

						tempObjectSb.AppendFormat("{0},{1},{2}", index1, index2, index3);
					}

					tempObjectSb.AppendLine();

					tempObjectSb.AppendLine("\t\t\t}");
					tempObjectSb.AppendLine("\t\t}");
				}

				// ================ UV CREATION =========================

				// -- UV 1 Creation
				int uvLength = mesh.uv.Length;
				Vector2[] uvs = mesh.uv;

				tempObjectSb.AppendLine("\t\tLayerElementUV: 0 {"); // the Zero here is for the first UV map
				tempObjectSb.AppendLine("\t\t\tVersion: 101");
				tempObjectSb.AppendLine("\t\t\tName: \"UVSet0\"");
				tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
				tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");
				tempObjectSb.AppendLine("\t\t\tUV: *" + uvLength * 2 + " {");
				tempObjectSb.Append("\t\t\t\ta: ");

				for (int i = 0; i < uvLength; i++)
				{
					if (i > 0)
						tempObjectSb.Append(",");

					tempObjectSb.AppendFormat("{0},{1}", FE.FBXFormat(uvs[i].x), FE.FBXFormat(uvs[i].y));
				}
				tempObjectSb.AppendLine();

				tempObjectSb.AppendLine("\t\t\t}");

				// UV tile index coords
				tempObjectSb.AppendLine("\t\t\tUVIndex: *" + triangleCount + " {");
				tempObjectSb.Append("\t\t\t\ta: ");

				for (int i = 0; i < triangleCount; i += 3)
				{
					if (i > 0)
						tempObjectSb.Append(",");

					// Triangles need to be fliped for the x flip
					int index1 = triangles[i];
					int index2 = triangles[i + 2];
					int index3 = triangles[i + 1];

					tempObjectSb.AppendFormat("{0},{1},{2}", index1, index2, index3);
				}

				tempObjectSb.AppendLine();

				tempObjectSb.AppendLine("\t\t\t}");
				tempObjectSb.AppendLine("\t\t}");

				// -- UV 2 Creation
				if (mesh.uv2 != null && mesh.uv2.Length != 0)
				{
					uvLength = mesh.uv2.Length;
					uvs = mesh.uv2;

					tempObjectSb.AppendLine("\t\tLayerElementUV: 1 {"); // the Zero here is for the first UV map
					tempObjectSb.AppendLine("\t\t\tVersion: 101");
					tempObjectSb.AppendLine("\t\t\tName: \"UVSet1\"");
					tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
					tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");
					tempObjectSb.AppendLine("\t\t\tUV: *" + uvLength * 2 + " {");
					tempObjectSb.Append("\t\t\t\ta: ");

					for (int i = 0; i < uvLength; i++)
					{
						if (i > 0)
							tempObjectSb.Append(",");

						tempObjectSb.AppendFormat("{0},{1}", FE.FBXFormat(uvs[i].x), FE.FBXFormat(uvs[i].y));

					}
					tempObjectSb.AppendLine();

					tempObjectSb.AppendLine("\t\t\t}");

					// UV tile index coords
					tempObjectSb.AppendLine("\t\t\tUVIndex: *" + triangleCount + " {");
					tempObjectSb.Append("\t\t\t\ta: ");

					for (int i = 0; i < triangleCount; i += 3)
					{
						if (i > 0)
							tempObjectSb.Append(",");

						// Triangles need to be fliped for the x flip
						int index1 = triangles[i];
						int index2 = triangles[i + 2];
						int index3 = triangles[i + 1];

						tempObjectSb.AppendFormat("{0},{1},{2}", index1, index2, index3);
					}

					tempObjectSb.AppendLine();

					tempObjectSb.AppendLine("\t\t\t}");
					tempObjectSb.AppendLine("\t\t}");
				}

				// TODO: Add more UV saving if FBX supports it

				// -- Smoothing
				// TODO: Smoothing doesn't seem to do anything when importing. This maybe should be added. -KBH

				// ============ MATERIALS =============

				tempObjectSb.AppendLine("\t\tLayerElementMaterial: 0 {");
				tempObjectSb.AppendLine("\t\t\tVersion: 101");
				tempObjectSb.AppendLine("\t\t\tName: \"\"");
				tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygon\"");
				tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");

				int totalFaceCount = 0;

				// So by polygon means that we need 1/3rd of how many indicies we wrote.
				int numberOfSubmeshes = mesh.subMeshCount;

				StringBuilder submeshesSb = new StringBuilder();

				// For just one submesh, we set them all to zero
				if (numberOfSubmeshes == 1)
				{
					int numFaces = triangles.Length / 3;

					for (int i = 0; i < numFaces; i++)
					{
						submeshesSb.Append("0,");
						totalFaceCount++;
					}
				}
				else
				{
					List<int[]> allSubmeshes = new List<int[]>();

					// Load all submeshes into a space
					for (int i = 0; i < numberOfSubmeshes; i++)
						allSubmeshes.Add(mesh.GetIndices(i));

					// TODO: Optimize this search pattern
					for (int i = 0; i < triangles.Length; i += 3)
					{
						for (int subMeshIndex = 0; subMeshIndex < allSubmeshes.Count; subMeshIndex++)
						{
							bool breaker = false;

							for (int n = 0; n < allSubmeshes[subMeshIndex].Length; n += 3)
							{
								if (triangles[i] == allSubmeshes[subMeshIndex][n]
								   && triangles[i + 1] == allSubmeshes[subMeshIndex][n + 1]
								   && triangles[i + 2] == allSubmeshes[subMeshIndex][n + 2])
								{
									submeshesSb.Append(subMeshIndex.ToString());
									submeshesSb.Append(",");
									totalFaceCount++;
									break;
								}

								if (breaker)
									break;
							}
						}
					}

					Debug.Log(submeshesSb[submeshesSb.Length - 1]);

					if (submeshesSb[submeshesSb.Length - 1] == ',')
						submeshesSb.Remove(submeshesSb.Length - 1, 1);
				}

				tempObjectSb.AppendLine("\t\t\tMaterials: *" + totalFaceCount + " {");
				tempObjectSb.Append("\t\t\t\ta: ");
				tempObjectSb.AppendLine(submeshesSb.ToString());
				tempObjectSb.AppendLine("\t\t\t} ");
				tempObjectSb.AppendLine("\t\t}");

				// ============= INFORMS WHAT TYPE OF LATER ELEMENTS ARE IN THIS GEOMETRY =================
				tempObjectSb.AppendLine("\t\tLayer: 0 {");
				tempObjectSb.AppendLine("\t\t\tVersion: 100");
				tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
				tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementNormal\"");
				tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
				tempObjectSb.AppendLine("\t\t\t}");
				tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
				tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementMaterial\"");
				tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
				tempObjectSb.AppendLine("\t\t\t}");
				tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
				tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementTexture\"");
				tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
				tempObjectSb.AppendLine("\t\t\t}");
				if (containsColors)
				{
					tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
					tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementColor\"");
					tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
					tempObjectSb.AppendLine("\t\t\t}");
				}
				tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
				tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementUV\"");
				tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
				tempObjectSb.AppendLine("\t\t\t}");
				// inserts UV2s into the file export
				if (mesh.uv2 != null && mesh.uv2.Length != 0)
				{
					tempObjectSb.AppendLine("\t\t}");
					tempObjectSb.AppendLine("\t\tLayer: 1 {");
					tempObjectSb.AppendLine("\t\t\tVersion: 100");
					tempObjectSb.AppendLine("\t\t\tLayerElement:  {");
					tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementUV\"");
					tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 1");
					tempObjectSb.AppendLine("\t\t\t}");
				}
				tempObjectSb.AppendLine("\t\t}");
				tempObjectSb.AppendLine("\t}");

				// Add the connection for the model to the geometry so it is attached the right mesh
				tempConnectionsSb.AppendLine("\t;Geometry::, Model::" + mesh.name);
				tempConnectionsSb.AppendLine("\tC: \"OO\"," + geometryId + "," + modelId);
				tempConnectionsSb.AppendLine();

			 
				{
					 

					int referenceId = 11;

					tempConnectionsSb.AppendLine("\t;Material::" + "null" + ", Model::" + mesh.name);
					tempConnectionsSb.AppendLine("\tC: \"OO\"," + referenceId + "," + modelId);
					tempConnectionsSb.AppendLine();
				}

			}

		 

			objects.Append(tempObjectSb.ToString());
			connections.Append(tempConnectionsSb.ToString());

			return modelId;
		}
	}
}
